﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;

namespace Wheels.Windows
{
    public static class UriExtractor
    {
#pragma warning disable 1591

        private static readonly string smCommonUriPattern = @"\b[A-Z][-\w]*[A-Z]:[^\s<""']+";
        private static readonly string smCommonUrlPattern = @"\b(f|ht)tps?:[^\s<""']+";
        private static readonly Regex smUriPattern = new Regex(string.Join("|", smCommonUrlPattern, smCommonUriPattern), RegexOptions.IgnoreCase | RegexOptions.ECMAScript);

        private static IEnumerable<string> DetectUriCandidates(this string source)
        {
            var matches = smUriPattern.Matches(source);

            return matches.Cast<Match>().Select(m => m.Value);
        }

        private class ExtractUriDelegates
        {
            public ExtractTextDelegate ExtractText { get; set; }
            public ExtractUrisDelegate ExtractUris { get; set; }

            public ExtractUriDelegates()
            {
                ExtractUris = s => s.DetectUriCandidates();
            }
        }

        private delegate string ExtractTextDelegate(IDataObject source);
        private delegate IEnumerable<string> ExtractUrisDelegate(string source);
        private delegate object ReplaceTextDelegate(IDataObject source, string format, string find, string replace);

        private static IEnumerable<IEnumerable<ExtractUriDelegates>> smExtractorFallbacks;

        private static Dictionary<string, ReplaceTextDelegate> smTextReplacers;

        static UriExtractor()
        {
            InitializeExtractors();
            InitializeReplacers();
        }

        private static void InitializeExtractors()
        {
            smExtractorFallbacks = new[]{
                new []{
                    new ExtractUriDelegates{
                        ExtractText = 
                        src => {
                            var text = src.GetData("HTML Format", false) as string;
                            text = text.GetFragmentHtml();
                            return text;
                        },
                        ExtractUris =
                        src => {
                            return src.DetectUriCandidates().Select(s => s.HtmlDecode());
                        },
                    },
                    new ExtractUriDelegates{
                        ExtractText = 
                        src => {
                            var text = src.GetStreamText("UniformResourceLocatorW", Encoding.Unicode);
                            return text;
                        },
                    },
                    new ExtractUriDelegates{
                        ExtractText = 
                        src => {
                            var text = src.GetStreamText("UniformResourceLocator");
                            return text;
                        },
                    },
                },
                new []{
                    new ExtractUriDelegates{
                        ExtractText = 
                        src => {
                            var text = src.GetData("UnicodeText", false) as string;
                            return text;
                        },
                    },
                    new ExtractUriDelegates{
                        ExtractText = 
                        src => {
                            var text = src.GetData("Text", false) as string;
                            return text;
                        },
                    },
                    new ExtractUriDelegates{
                        ExtractText = 
                        src => {
                            var text = src.GetData("OEMText", false) as string;
                            return text;
                        },
                    },
                },
            };
        }

        private static void InitializeReplacers()
        {
            smTextReplacers = new Dictionary<string, ReplaceTextDelegate>
            {
            };
        }

        private static bool IsKnownFormat(string format)
        {
            return false;
        }

        public static Dictionary<string, Func<IDataObject, IDataObject>> GetExtractActions(this IDataObject source)
        {
            var known = new HashSet<string>();
            var tasks = new Dictionary<string, Func<IDataObject, IDataObject>>();

            var encoding = source.GetEncoding() ?? Encoding.Default;

            foreach (var group in smExtractorFallbacks)
            {
                foreach (var func in group)
                {
                    var extractor = func;
                    var inputText = extractor.ExtractText(source);
                    if (inputText.IsNullOrWhiteSpace())
                        continue; // foreach (var func in group)

                    var text = inputText.Trim();
                    if (!known.Contains(text))
                    {
                        known.Add(text);

                        foreach (var sourceUri in extractor.ExtractUris(text))
                        {
                            foreach (var uri in sourceUri.ExtractUriList())
                            {
                                var argUri = uri;
                                if (tasks.ContainsKey(argUri.OriginalString))
                                    continue; // foreach (var arg in args)

                                tasks.Add(argUri.OriginalString, src =>
                                {
                                    if (extractor.ExtractText(src) != inputText)
                                        return null;

                                    var result = new DataObject(argUri.OriginalString);

                                    var locale = src.GetData("Locale", false);
                                    if (locale != null)
                                    {
                                        var buffer = BitConverter.GetBytes(CultureInfo.CurrentCulture.LCID);
                                        locale = new MemoryStream(buffer);
                                    }
                                    result.SetData("Locale", locale, false);

                                    result.SetData("OEMText", argUri.OriginalString, false);

                                    var escapedUri = argUri.OriginalString.HtmlEncode();
                                    var title = src.GetStreamText("UniformResourceLocatorW", Encoding.Unicode) ?? src.GetStreamText("UniformResourceLocatorW", encoding);
                                    if (!title.IsNullOrWhiteSpace())
                                    {
                                        using (var sr = new StringReader(title))
                                            while (sr.Peek() > 0)
                                                title = sr.ReadLine();

                                        if (title.DetectUriCandidates().Any())
                                            title = null;
                                    }
                                    if (title.IsNullOrWhiteSpace())
                                        title = escapedUri;
                                    else
                                        title = title.HtmlEncode();

                                    result.SetData("HTML Format", ("<a href=\"" + escapedUri + "\">" + title + "</a>").MakeHtmlFragment(), false);

                                    return result;
                                });
                            }
                        }

                        break; // foreach (var func in group)
                    }
                    //}
                }
            }

            return tasks;
        }
    }
}
